﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;

using Microsoft.Win32;

using MyUtils;
using MyUtils.UI;
using XTrace.Registry;
using XTrace.Viewer.Filter;
using XTrace.Viewer.Properties;

namespace XTrace.Viewer
{
	class WindowContext : ObservableObject, IDisposable
	{
		readonly FileSystemWatcher _watcher = new FileSystemWatcher();

		public WindowContext()
		{
			_filterDelete = new DelegateCommand(x => _filterModelStorage.Delete((FilterModel)x));
			_filterSave = new DelegateCommand(x => _filterModelStorage.Save((FilterModel)x), x => x != null && ((FilterModel)x).IsDirty);

			var file = Environment.GetCommandLineArgs().Skip(1).FirstOrDefault() ?? string.Empty;
			var srcs = new SourcesAutoViewModel();
			_sources = srcs;
			if (File.Exists(file))
			{
				srcs.AddExplicit(file);
			}
			Sources.PropertyChanged += SourcesPropertyChanged;

			if(_sources.SelectedSource!=null)
			{
				ThreadPool.QueueUserWorkItem(delegate
				{
					Application.Current.Dispatcher.BeginInvoke((Action)delegate
					{
						SwitchToSource(_sources.SelectedSource);
					}, DispatcherPriority.ApplicationIdle);
				});
			}
		}

		void SourcesPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName.IsNullOrEmpty() || e.PropertyName == "SelectedSource")
			{
				var ss = Sources.SelectedSource;
				SwitchToSource(ss);
			}
		}

		Stream _stream;
		StreamReader _reader;

		void SwitchToSource(SourceViewModel source)
		{
			_watcher.EnableRaisingEvents = false;
			Events.Clear();
			foreach (var ssfvm in StringSetFilters)
			{
				ssfvm.Clear();
			}

			if (source != null)
			{
				var fullPath = source.FullPath;
				bool ok = false;
				try
				{
					_stream = File.Open(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
					long max = (InfoSize)"5 MiB"; // default value

					try
					{
						max = _sources.SizeLimit;
					}
					catch (Exception ex)
					{
						Trace.Exception(ex, "Invalid Size Limit. Use default.", "{0}", max);
					}

					if (_stream.Length > max)
					{
						_stream.Position = _stream.Length - max;
					}
					_reader = new StreamReader(_stream);
					ReadAllRest(true);
					ok = true;
				}
				catch (Exception ex)
				{
					Trace.Exception(ex);
				}

				if (ok)
				{
					_watcher.BeginInit();
					_watcher.Path = Path.GetDirectoryName(fullPath);
					_watcher.Filter = Path.GetFileName(fullPath);
					_watcher.InternalBufferSize = 256 * 1024;
					_watcher.Changed += WatcherChanged;
					_watcher.NotifyFilter = NotifyFilters.Size;
					_watcher.EndInit();
					_watcher.EnableRaisingEvents = true;
				}
			}
		}

		public void ReadAllRest()
		{
			ReadAllRest(false);
		}
		
		public void ReadAllRest(bool toNew)
		{
			if (_reader == null)
			{
				return;
			}

			IList<XTraceEvent> events;
			if (toNew)
			{
				events = new List<XTraceEvent>(1024);
			}
			else
			{
				events = Events;
			}

			bool someNewLines = false;
			bool probablyPartOfLine = true;
			while (!_reader.EndOfStream)
			{
				try
				{
					var line = _reader.ReadLine();
					XTraceEvent ev;
					try
					{
						ev = XTraceEvent.Deserialize(line);
					}
					catch
					{
						if (probablyPartOfLine)
						{
							ev = null;
						}
						else
						{
							throw;
						}
					}
					probablyPartOfLine = false;
					if (ev != null)
					{
						if (ev.Level <= _sources.Level)
						{
							foreach (var ssfvm in _stringSetFilters)
							{
								ssfvm.AddEntry(ssfvm.FieldGetter(ev));
							}
							// AddEventId(ev.EventId);
							events.Add(ev);
							someNewLines = true;
						}
					}
				}
				catch (Exception ex)
				{
					Trace.Exception(ex);
				}
			}

			if (toNew)
			{
				Events = new ObservableCollection<XTraceEvent>(events);
			}

			if (Settings.Default.Follow && someNewLines)
			{
				SelectLastRow();
			}
		}

		public void SelectLastRow()
		{
			var events = EventsView;
			events.MoveCurrentToLast();
			
		}

//		int _eventsSelectedIndex;
//		public int EventsSelectedIndex
//		{
//			get { return _eventsSelectedIndex; }
//			set
//			{
//				_eventsSelectedIndex = value;
//				OnPropertyChanged("EventsSelectedIndex");
//				if (Settings.Default.Follow)
//				{
//					Dispatcher.CurrentDispatcher.BeginInvoke((Action)SelectLastRow, DispatcherPriority.Background);
//				}
//			}
//		}

		readonly ObservableCollection<StringSetFilterViewModel> _stringSetFilters = new ObservableCollection<StringSetFilterViewModel>
		{
			new StringSetFilterViewModel("EventId"),
			new StringSetFilterViewModel("Project"),
			new StringSetFilterViewModel("ProcessName"),
			new StringSetFilterViewModel("ThreadName"),
			new StringSetFilterViewModel("SourceName"),
			new StringSetFilterViewModel("SourceFile"),
			new StringSetFilterViewModel("NameSpace"),
			new StringSetFilterViewModel("ClassName"),
			new StringSetFilterViewModel("MethodName"),
		};

		void WatcherChanged(object sender, FileSystemEventArgs e)
		{
			Application.Current.Dispatcher.BeginInvoke((Action)ReadAllRest);
		}

		readonly XTraceSource Trace = new XTraceSource("WindowContext");

		ObservableCollection<XTraceEvent> _events = new ObservableCollection<XTraceEvent>();

		public ObservableCollection<XTraceEvent> Events
		{
			get { return _events; }
			set
			{
				if (_events != value)
				{
					_events = value;
					_eventsViewSource = null;
					OnPropertyChanged("Events");
					OnPropertyChanged("EventsView");
				}
			}
		}

		void EventsViewSourceFilter(object sender, FilterEventArgs e)
		{
			e.Accepted = Filter(e.Item);
		}

		bool Filter(object obj)
		{
			var ev = obj as XTraceEvent;
			return !ShowFilter || AdditionalFilterPassed(ev);
		}

		//ICollectionView _eventsView;
		CollectionViewSource _eventsViewSource;
		public ICollectionView EventsView
		{
			get
			{
				if (_eventsViewSource == null)
				{
					_eventsViewSource = new CollectionViewSource { Source = Events };
					_eventsViewSource.Filter += EventsViewSourceFilter;
				}
				return _eventsViewSource.View;
			}
		}


		readonly SourcesViewModel _sources;

		public SourcesViewModel Sources
		{
			get { return _sources; }
		}

		bool _showFilter;

		public bool ShowFilter
		{
			get { return _showFilter; }
			set
			{
				if (_showFilter != value)
				{
					_showFilter = value;
					OnPropertyChanged("ShowFilter");
				}
			}
		}

		#region IDisposable Members

		public void Dispose()
		{
			GC.SuppressFinalize(this);
			Dispose(true);
		}

		~WindowContext()
		{
			Dispose(false);
		}

		#endregion

		void Dispose(bool managed)
		{
			if (managed)
			{
			}

			CloseStreams();
		}

		void CloseStreams()
		{
			_watcher.EnableRaisingEvents = false;
			var stream = _stream;
			if (stream != null)
			{
				try
				{
					stream.Dispose();
				}
				catch (Exception ex)
				{
					Trace.Exception(ex);
				}
				_stream = null;
			}
			var reader = _reader;
			if (reader != null)
			{
				try
				{
					reader.Dispose();
				}
				catch (Exception ex)
				{
					Trace.Exception(ex);
				}
				_reader = null;
			}
		}

		public void MarkForClear()
		{
			var src = Sources;
			if (src != null)
			{
				var selected = src.SelectedSource;
				if (selected != null)
				{
					var path = Sources.SelectedSource.FullPath;
					CloseStreams();
					SourcesProvider.MarkForClear(path);
					if (File.Exists(path))
					{
						SwitchToSource(selected);
						Events.Clear();
					}
				}
			}
		}

		public void MarkForClearAll()
		{
			var src = Sources;
			if (src != null)
			{
				CloseStreams();
				Events.Clear();
				SwitchToSource(null);

				SourceViewModel[] srcs;
				lock(src.Sources)
				{
					srcs = src.Sources.ToArray();
				}
				foreach (var source in srcs)
				{
					var path = source.FullPath;
					SourcesProvider.MarkForClear(path);
				}

			}
		}

		private FilterModel _filterModel;
		private readonly FilterStorage _filterModelStorage = new FilterStorage();

		public FilterModel FilterModel
		{
			get { return _filterModel ?? (FilterModel = _filterModelStorage.GetAnything()); }
			set
			{
				if (_filterModel != value)
				{
					_filterModel = value;
					if (value != null)
					{
						if (_filterModel.DisplayName == Resources.FilterNew)
						{
							var flts = _filterModelStorage.Filters.ToArray();
							int n = 0;
							string name;
							do
							{
								n++;
								name = Resources.FilterNew + ' ' + n;
							} while (flts.Any(x => x.DisplayName == name));
							_filterModel.DisplayName = name;
							_filterModelStorage.Save(_filterModel);
						}

						UpdateFilterView();

						Settings.Default.LastFilterUsed = value.DisplayName;
					}
					OnPropertyChanged("FilterModel");
				}
			}
		}

		void UpdateFilterView()
		{
			foreach (var stringSetFilterViewModel in StringSetFilters)
			{
				stringSetFilterViewModel.FilterModel = FilterModel;
			}
		}

		public ICommand _filterDelete;

		public ICommand FilterDelete
		{
			get { return _filterDelete; }
		}

		public ICommand _filterSave;

		public ICommand FilterSave
		{
			get { return _filterSave; }
		}

		string _filterInfoRegexCachedLastQuery;

		Regex _filterInfoRegexCached;
		public Regex FilterInfoRegexCached
		{
			get
			{
				if (_filterInfoRegexCachedLastQuery != FilterModel.FilterInfoRegex)
				{
					_filterInfoRegexCachedLastQuery = FilterModel.FilterInfoRegex;
					if (_filterInfoRegexCachedLastQuery == null)
					{
						_filterInfoRegexCached = null;
					}
					else
					{
						_filterInfoRegexCached = new Regex(_filterInfoRegexCachedLastQuery,
						                                   RegexOptions.Compiled | RegexOptions.CultureInvariant |
						                                   RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
					}
				}
				return _filterInfoRegexCached;
			}
		}

		public FilterStorage FilterStorage
		{
			get { return _filterModelStorage; }
		}

		internal void Reopen()
		{
			SwitchToSource(_sources.SelectedSource);
		}

		public bool AdditionalFilterPassed(XTraceEvent ev)
		{
			var f = FilterModel;
			if (f.FilterProcessId != 0)
			{
				if (ev.ProcessId != f.FilterProcessId)
				{
					return false;
				}
			}
			if (f.FilterThreadId != 0)
			{
				if (ev.ThreadId != f.FilterThreadId)
				{
					return false;
				}
			}

			foreach (var ssfvm in StringSetFilters)
			{
				var ssf = f.GetStringSetFilter(ssfvm.FieldName);
				if (ssf.UseIncludes && ssf.IncludesHashSet.Count > 0)
				{
					// возвращаем false если в списке инклюдов нет указанного
					if (!ssf.IncludesHashSet.Contains(ssfvm.FieldGetter(ev)))
					{
						return false;
					}
				}

				if (ssf.UseExcludes)
				{
					// возвращаем false если в списке ексклюдов есть указанный
					if (ssf.ExcludesHashSet.Contains(ssfvm.FieldGetter(ev)))
					{
						return false;
					}
				}
			}

			if (FilterInfoRegexCached != null)
			{
				if (!FilterInfoRegexCached.Match(ev.AdditionalInformation).Success)
				{
					return false;
				}
			}
			return true;
		}

		public ObservableCollection<StringSetFilterViewModel> StringSetFilters
		{
			get { return _stringSetFilters; }
		}

//		CollectionViewSource _stringSetFiltersViewSource;

//		public ICollectionView StringSetFiltersView
//		{
//			get
//			{
//				if (_stringSetFiltersViewSource == null)
//				{
//					_stringSetFiltersViewSource = new CollectionViewSource
//					{
//						Source = _stringSetFilters,
//					};
//					_stringSetFiltersViewSource.Filter += _stringSetFiltersView_Filter;
//				}
//				return _stringSetFiltersViewSource.View;
//			}
//		}

//		void _stringSetFiltersView_Filter(object sender, FilterEventArgs e)
//		{
//			var filter = (StringSetFilterViewModel)e.Item;
//			Trace.Important("Filter", "Name '{0}', EntriesNonFiltered '{1}'", filter.FieldName, filter.EntriesNonFiltered.Count);
//			// e.Accepted = filter.EntriesNonFiltered.Count > 0;
//		}

		public ICommand EventsRefilter
		{
			get { return new DelegateCommand(x => Refilter()); }
		}

		public void Refilter()
		{
			_eventsViewSource.View.Refresh();
		}

		public ICommand MenuOpen
		{
			get { return new DelegateCommand(x=>Open()); }
		}

		public void Open()
		{
			var ofd = new OpenFileDialog();
			if (ofd.ShowDialog() == true && File.Exists(ofd.FileName))
			{
				_sources.SwitchTo(ofd.FileName);
			}
		}

		StringSetFilterViewModel _selectedStringSetFilter;

		public StringSetFilterViewModel SelectedStringSetFilter
		{
			get { return _selectedStringSetFilter ?? (_selectedStringSetFilter = StringSetFilters.FirstOrDefault()); }
			set
			{
				if (_selectedStringSetFilter != value)
				{
					_selectedStringSetFilter = value;
					OnPropertyChanged("SelectedStringSetFilter");
				}
			}
		}
	}

}